package apig.algorithm.tokenizer;

import java.util.*;

/**
 * <b> Note:</b> Thread SAFE
 * <p>
 * <b> Note:</b> Doesn't create threads.
 * 
 * @author Alex
 * 
 */

public class JavaTokenizer implements Tokenizer {

	private class JavaToken implements Token {

		int id;
		int start;
		int end;

		public JavaToken(int id, int start, int end) {
			this.id = id;
			this.start = start;
			this.end = end;
		}

		@Override
		public int getID() {
			return id;
		}

		@Override
		public int getStart() {
			return start;
		}

		@Override
		public int getEnd() {
			return end;
		}

		@Override
		public String toString() {
			return "JavaToken [id=" + id + ", start=" + start + ", end=" + end
					+ "]";
		}
	}

	private static final int IDENTIFIER_TOKEN = 1;
	private static final int LITERAL_TOKEN = 2;
	/*
	 * Not used: "abstract",
	 * "package","synchronized","goto","private","protected" ,"import",
	 * "public","transient","final","strictfp","volatile","const",
	 */
	private static final int KEYWORD_FIRST_TOKEN = 3;
	private static final List<String> KEYWORD_LIST = Arrays
			.asList(new String[] { "continue", "for", "new", "switch",
					"assert", "default", "if", "boolean", "do", "this",
					"break", "double", "implements", "throw", "byte", "else",
					"throws", "case", "enum", "instanceof", "return", "catch",
					"extends", "int", "short", "try", "char", "interface",
					"static", "void", "class", "finally", "long", "float",
					"native", "super", "while" });
	private static final Map<String, Integer> KEYWORD_MAP = new HashMap<String, Integer>();
	static {
		for (int i = 0; i < KEYWORD_LIST.size(); ++i) {
			KEYWORD_MAP.put(KEYWORD_LIST.get(i), i + KEYWORD_FIRST_TOKEN);
		}
	}
	private static final int KEYWORD_LAST_TOKEN = KEYWORD_FIRST_TOKEN
			+ KEYWORD_LIST.size() - 1;
	private static final int SIGN_FIRST_TOKEN = KEYWORD_LAST_TOKEN + 1;
	public static final int LAST_TOKEN = SIGN_FIRST_TOKEN;
	/*
	 * Not used: "{", "}", "@", ".",
	 */
	private static final List<String> SIGN_LIST = Arrays.asList(">>>=", "...",
			"<<=", ">>=", ">>>", "==", "<=", ">=", "!=", "&&", "||", "++",
			"--", "<<", ">>", "+=", "-=", "*=", "/=", "&=", "|=", "^=", "%=",
			"=", ">", "<", "!", "~", "?", ":", "+", "-", "*", "/", "&", "|",
			"^", "%", "(", ")", "[", "]", ",", ";");
	private static final Map<String, Integer> SIGN_MAP = new HashMap<String, Integer>();
	static {
		for (int i = 0; i < SIGN_LIST.size(); ++i) {
			SIGN_MAP.put(SIGN_LIST.get(i), i + SIGN_FIRST_TOKEN);
		}
	}
	public static final int SIGN_LAST_TOKEN = SIGN_FIRST_TOKEN
			+ SIGN_LIST.size();

	@Override
	public Token[] parse(String code) throws MalformedCodeException {
		List<Token> tokens = new ArrayList<Token>();
		JavaStringTokenizer jts = new JavaStringTokenizer(code);
		jts.nextToken();
		for (;;) {
			while (jts.getType() != JavaStringTokenizer.EOF
					&& !jts.getString().equals("class"))
				jts.nextToken();
			while (jts.getType() != JavaStringTokenizer.EOF
					&& !jts.getString().equals("{"))
				jts.nextToken();
			if (jts.getType() == JavaStringTokenizer.EOF)
				break;
			int lev = 1;
			jts.nextToken();
			while (jts.getType() != JavaStringTokenizer.EOF && lev > 0) {
				if (jts.getType() == JavaStringTokenizer.IDENTIFIER) {
					JavaToken tok = new JavaToken(IDENTIFIER_TOKEN,
							jts.getBegin(), jts.getEnd());
					for (;;) {
						jts.nextToken();
						if (!jts.getString().equals("."))
							break;
						if (jts.nextToken() != JavaStringTokenizer.IDENTIFIER)
							break;
						tok.end = jts.getEnd();
					}
					tokens.add(tok);
				}
				if (jts.getType() == JavaStringTokenizer.LITERAL) {
					tokens.add(new JavaToken(LITERAL_TOKEN, jts.getBegin(), jts
							.getEnd()));
					jts.nextToken();
				}
				if (jts.getType() == JavaStringTokenizer.KEYWORD) {
					Integer id = KEYWORD_MAP.get(jts.getString());
					if (id != null)
						tokens.add(new JavaToken(id, jts.getBegin(), jts
								.getEnd()));
					jts.nextToken();
				}
				if (jts.getType() == JavaStringTokenizer.SIGN) {
					if ("{".equals(jts.getString()))
						++lev;
					if ("}".equals(jts.getString()))
						--lev;
					Integer id = SIGN_MAP.get(jts.getString());
					if (id != null)
						tokens.add(new JavaToken(id, jts.getBegin(), jts
								.getEnd()));
					boolean semicolon = jts.getString().equals(";");
					jts.nextToken();
					if (semicolon)
						while (jts.getString().equals(";"))
							jts.nextToken();
				}
			}
		}
		return tokens.toArray(new Token[0]);
	}

	// public static void main(String[] args) throws Exception {
	// BufferedReader r = new BufferedReader(
	// new FileReader(
	// "D:\\workspace\\API\\test.java"));
	// StringBuilder sb = new StringBuilder();
	// try {
	// String line;
	// while ((line = r.readLine()) != null)
	// sb.append(line + "\n");
	// } finally {
	// r.close();
	// }
	// System.out.println(JavaTokenizer.SIGN_LAST_TOKEN);
	// JavaTokenizer jt = new JavaTokenizer();
	// String s = sb + "";
	// Token[] tokens = jt.parse(s);
	// for (Token t : tokens) {
	// System.out.println(s.substring(t.getStart(), t.getEnd()) + "\t"
	// + t.getID());
	// }
	// }
}
